﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Dapper;
using MySql.Data.MySqlClient;
using YmtAuth.Common;
using YmtSystem.CrossCutting;

namespace YmtAuth.Repository.MySQL
{
    public class ParametersWrapper
    {
        private readonly DynamicParameters par;
        public ParametersWrapper()
        {
             par=new DynamicParameters();   
        }

        public static ParametersWrapper Factory {
            get { return new ParametersWrapper();}
        }

        public ParametersWrapper Add(string name, object val, DbType type)
        {
            par.Add(name,val,type);
            return this;
        }

        public ParametersWrapper Add(string name, object val)
        {
            par.Add(name,val);
            return this;
        }

        public ParametersWrapper AddXml(string name, object[] val)
        {
            par.Add(name, ParseArrayValue(val), DbType.Xml);            
            return this;
        }

        public ParametersWrapper AddLong(string name,Int64 val)
        {
            par.Add(name,val,DbType.Int64);
            return this;
        }
        public ParametersWrapper AddInt(string name, Int32 val)
        {
            par.Add(name, val, DbType.Int32);
            return this;
        }
        public ParametersWrapper AddString(string name, string val)
        {
            par.Add(name, val, DbType.String);
            return this;
        }

        public ParametersWrapper AddByte(string name, byte val)
        {
            par.Add(name,val,DbType.Byte);
            return this;
        }

        public ParametersWrapper AddDateTime(string name, DateTime time)
        {
            par.Add(name,time,DbType.DateTime);
            return this;
        }

        public ParametersWrapper AddDateTime2(string name, DateTime time)
        {
            par.Add(name,time,DbType.DateTime2);
            return this;
        }

        public DynamicParameters Build()
        {
            return this.par;
        }
        private string ParseArrayValue(object[] value)
        {
            var str = new StringBuilder("<root>");
            foreach (var val in value)
            {
                str.Append("<k v='").Append(val).Append("'/>");
            }
            str.Append("</root>");
            return str.ToString();
        }
    }

    public static class _Extend
    {
       public static DynamicParameters ToParamNames<TEntity>(this TEntity o)
        {
            var tmpParameters = ParametersWrapper.Factory;
            TEntity tmpEntity = (TEntity)System.Activator.CreateInstance(typeof(TEntity), true);
            foreach (
                var prop in
                    typeof(TEntity).GetProperties(BindingFlags.GetProperty | BindingFlags.Instance |
                                                   BindingFlags.Public))
            {
                var attribs = prop.GetCustomAttributes(typeof(IgnorePropertyAttribute), true);
                var attr = attribs.FirstOrDefault() as IgnorePropertyAttribute;
                if (attr == null || (attr != null && !attr.Value))
                {
                    tmpParameters.Add(string.Format("@{0}", prop.Name), prop.GetValue(o));
                }
            }
            return tmpParameters.Build();
        }
       public static DynamicParameters ToDapperParameters(this IDictionary<string, object> @where)
       {
           var parameters = new DynamicParameters();
           foreach (var val in @where)
           {
               parameters.Add(val.Key, val.Value);
           }
           return parameters;
       }
       public static DynamicParameters ToDapperParameters(this DynamicParameters @params , IDictionary<string, object> paramsDic)
       {
           foreach (var val in paramsDic)
           {
               @params.Add(val.Key, val.Value);
           }
           return @params;
       }

        public static DynamicParameters ToDapperParameters(this SqlParameter[] parameter)
        {
            if (parameter == null) return null;

            var dp = new DynamicParameters();
            foreach (var item in parameter)
                dp.Add(item.ParameterName, item.Value, item.DbType);

            return dp;
        }

        public static DynamicParameters ToDapperParameters(this MySqlParameter[] parameter)
       {
           if (parameter == null) return null;

           var dp = new DynamicParameters();
           foreach (var item in parameter)
               dp.Add(item.ParameterName, item.Value,item.DbType);

           return dp;
       }
       public static IEnumerable<TFirst> Map<TFirst, TSecond, TGroupByKey>(
          this SqlMapper.GridReader reader,
          Func<TFirst, TGroupByKey> firstKey,
          Func<TSecond, TGroupByKey> secondKey,
          Action<TFirst, IEnumerable<TSecond>> addChildren
          )
       {
           if (reader.IsConsumed) return new TFirst[0];
           //父对象
           var first = reader.Read<TFirst>();
           if (reader.IsConsumed) return first;
           //子对象
           var childMap = reader
               .Read<TSecond>()
               .GroupBy(s => secondKey(s))
               .ToDictionary(g => g.Key, g => g.AsEnumerable());

           foreach (var item in first)
           {
               IEnumerable<TSecond> children;
               if (childMap.TryGetValue(firstKey(item), out children))
               {
                   addChildren(item, children);
               }
           }
           return first;
       }
    }

    public static class MySQLUtility
    {
        public static TResponse TryExecute<TResponse>(Func<TResponse> func, TResponse defVal = default(TResponse),int retry=0,string desc=null)
        {
            try
            {
                return func();
            }
            catch (MySqlException ex)
            {
                YmatouLoggingService.Error("TryExecute MySqlException ex {0},{1}", desc, ex);
                return defVal;
            }
            catch (Exception ex)
            {
                YmatouLoggingService.Error("TryExecute MySqlException(Exception) ex {0},{1}", desc, ex);
                return defVal;
            }
        }
    }
}
